package connect4client.net.socket;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import messages.*;
import connect4client.core.Controller;
import connect4client.net.ConnectionController;
import connect4client.net.servers.Server;

public class ConnectionControllerTCP extends ConnectionController {

	
	public ConnectionControllerTCP(Controller controller, Server server) throws IOException {
		super(controller, server);
		
		socket = new Socket(server.getIp(), server.getPort());

		out = new ObjectOutputStream(socket.getOutputStream());
		in = new ObjectInputStream(socket.getInputStream());

		new Thread(this, this.getClass().getCanonicalName()).start();
	}
	

	@Override
	public void sendMessage(Message message) {
		try {
			out.reset();
			out.writeObject(message);
		} catch (Exception e) {
			System.err.println("Sending of message failed!");
			if (!reconnecting)
				reconnect();
		}
	}

	
	@Override
	public void sendMessageOrFail(Message message) throws IOException {
		out.reset();
		out.writeObject(message);
	}
	

	public void run() {
		udpService = new UDPService();
		new Thread(udpService).start();
		
		while (true) {
			try {
				Message message = (Message) in.readObject();			
				controller.getMessageConsumer().putMessage(message);
				
				if (message instanceof LoginFailedMessage)
					return;
				
			} catch (Exception e) {				
				e.printStackTrace();
				if (!reconnecting)
					reconnect();
			}
		}
	}
	
		
	public boolean retry() {
		try {
			socket = new Socket(runningServer.getIp(), runningServer
					.getPort());
			out = new ObjectOutputStream(socket.getOutputStream());
			in = new ObjectInputStream(socket.getInputStream());			
		} catch (IOException ex) {
			return false;
		}
		
		return true;
	}
	
	
	@Override
	public void sendWallMessage(String contents){
		udpService.sendMessage(new WallMessage(controller.getUser().getLogin(), contents));
	}
		
	
	private Socket socket;
	private ObjectInputStream in;
	private ObjectOutputStream out;
	private UDPService udpService;
	
	
	private class UDPService implements Runnable {

		public UDPService() {
			try {
				socket = new DatagramSocket();			
				multicastSocket = new MulticastSocket(MULTICAST_PORT);
				InetAddress group = InetAddress.getByName(GROUP_IP);
				multicastSocket.joinGroup(group);					
			} catch (UnknownHostException e) {		
				e.printStackTrace();
			} catch (IOException e) {			
				e.printStackTrace();
			}
		}
		
		
		public void sendMessage(Message message){
	        try {
				ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);	       
		        ObjectOutputStream os = new ObjectOutputStream(new BufferedOutputStream(byteStream));
		        
				os.flush(); 
		        os.writeObject(message); 
		        os.flush(); 
		        
		        byte[] sendBuf = byteStream.toByteArray(); 
		        DatagramPacket packet = new DatagramPacket(sendBuf, sendBuf.length, ConnectionControllerTCP.this.runningServer.getIp(), 7776); 
		        
		        System.err.println("UDP: Sending " + message);
		        
		        socket.send(packet); 
		        os.close(); 
			} catch (IOException e) {			
				e.printStackTrace();
			} 
		}
		
		
		public void run() {
			while (true) {
				try {
					byte[] recvBuf = new byte[1024];
					DatagramPacket packet = new DatagramPacket(recvBuf,
							recvBuf.length);
					multicastSocket.receive(packet);
					
					ByteArrayInputStream byteStream = new ByteArrayInputStream(
							recvBuf);
					ObjectInputStream is = new ObjectInputStream(
							new BufferedInputStream(byteStream));
					
					Message message = (Message) is.readObject();
					controller.forward(message);
				} catch (IOException e) {					
					e.printStackTrace();
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
		
		private DatagramSocket socket;	
		private MulticastSocket multicastSocket;
		
		private final int MULTICAST_PORT = 32001;
		private final String GROUP_IP = "225.0.0.0";
	}
}
